#!/usr/bin/env python3
# Created by han on 2016/7/4
import json
import os
import re
import socketserver
import subprocess
import sys

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import tools


class Hftp(socketserver.BaseRequestHandler):
    def handle(self):
        home_name = self.request.recv(4096)  #用户只要连接到服务器就会传递过来一个用户家目录的名字
        self.request.send(bytes("欢迎使用Hftp", "utf8"))
        while True:
            data = self.request.recv(4096)          #接受客户端发来的命令
            client_data = data.decode()     #将用户传过来的bypes形式转换
            print(data,len(data))
            if len(data) == 0:                      #如果对方挂断，则执行这里
                print("----挂断")
                break
            print("[%s] input: %s" % (self.client_address, client_data))   #log
            if not re.findall("^\{.*\}$",client_data):    #如果客户端传过来的不是json格式，就是执行命令，
                self.task_cmd(client_data, home_name)
                continue
            task_data = json.loads(client_data)     #如果是json肯定就是注册上传和下载
            task_type = task_data["action"]         #获取用户的动作
            if hasattr(self, "task_%s" % task_type):        #判断没有这个功能
                func = getattr(self, "task_%s" % task_type) #有就执行
                func(task_data, home_name)

    def task_cmd(self, client_cmd, home_name):

        cmd_data = subprocess.Popen("%s ../space/%s" % (client_cmd, home_name.decode()), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        ret = cmd_data.stdout.read()  # python3 中默认返回的就是bytes类型
        if not ret:                   #如果没有正常输出，就是命令有问题
            ret = cmd_data.stderr.read()        #把错误信息读出来
        if len(ret) == 0:  # 执行命令的结果        #如果长度为零，就为这个命令没有返回值
            ret = bytes("none", "utf8")
        ready_data = "ready|%d" % len(ret)      #开始准备传输，解决粘包问题
        print(ready_data)
        self.request.sendall(bytes(ready_data, "utf8"))     #传输这个准备数据
        client_ready = self.request.recv(4096)
        print('-----client_ready', client_ready.decode())
        if client_ready.decode().startswith("start"):
            self.request.sendall(ret)

    def task_put(self,task_data,home_name):
        home_list = home_name.decode().split("_")
        file_name = task_data["name"].split("\\")[-1]       #获取用户名字
        file_size = task_data["size"]       #获取文件数据代销
        # abs_file_name = home_name.decode() + "/" + file_name
        abs_file_name = "../space/" + home_name.decode() + "/" + file_name
        print("=======",abs_file_name)
        file_md5 = task_data["md5"]         #获取文件的原始md5值
        home_size = int(home_list[1])*1024*1024       # home_size 单位是 b  获取家目录大小，家目录大小单位为M，用户上传文件是需要做一个单位转换
        dir_size = tools.dir_size(home_name)          #  may_size 单位是 b       获取用户家目录当前大小
        may_size = home_size - dir_size               #家目录大小 - 已经用了的大小，就是还能上传的大小
        print(file_size,may_size)
        if file_size > may_size:                      #如果文件大于还能上传的大小
            msg = {"status": "3","file": file_size,"max": may_size}
            msg_str = json.dumps(msg)
            self.request.sendall(bytes(msg_str, "utf8"))
            return "no_size"
        if not os.path.exists(abs_file_name):       #如果这个文件不存在
            msg = json.dumps({"status": "1","seek": 0})
            self.request.sendall(bytes(msg,"utf8")) #发送确认信息
            recv_size = 0                           #初始化接受文件大小
            with open(abs_file_name,"wb") as local_file:        #新建一个要存储的文件
                while recv_size < file_size:                    #如果接受的大小小于文件的大小
                    data = self.request.recv(4096)              #那么就接收
                    recv_size += len(data)                      #将接收大小累加
                    local_file.write(data)                      #往文件中写
                    print(data)
                    print("file_size %s , recv_size %s" % (file_size,recv_size))
            recv_md5 = tools.get_file_md5(abs_file_name)    #接收后文件的md5值
            if recv_md5 == file_md5:                        #对比两个文件的md5值
                msg = "success"
                self.request.sendall(bytes(msg, "utf8"))    #发送成功消息
                tools.color_msg(32, msg, 40)
            else:
                msg = "incomplete"
                self.request.sendall(bytes(msg, "utf8"))    #发送残缺消息
                tools.color_msg(32, msg, 40)
        else:                                               #如果已经存在这个文件
            local_md5 = tools.get_file_md5(abs_file_name)   #获取本地文件的md5
            if local_md5 == file_md5:                       #校验md5值一样
                msg = json.dumps({"status": "0", "seek":0}) #表示文件完整，不用传输
                self.request.sendall(bytes(msg,"utf8"))
            else:                                           #如果不一样
                with open(abs_file_name,"a+b") as local_file:      #以追加字节的模式打开
                    before_pos = local_file.tell()                 #找到当前文件最后的位置
                    recv_size = os.stat(abs_file_name).st_size
                    msg = json.dumps({"status": "2", "seek": before_pos, "server_size": recv_size})  #server_size : server端把现有文件的大小也传给客户端
                    self.request.sendall(bytes(msg, "utf8")) #将这个位置传给客户端
                    while recv_size < file_size:            #收到的大小小于文件大小时
                        data = self.request.recv(4096)      #循环接受
                        recv_size += len(data)              #继续加上数据的大小
                        local_file.write(data)              #追加写入文件
                        print("file_size %s , recv_size %s" % (file_size, recv_size))
                recv_md5 = tools.get_file_md5(abs_file_name)
                if recv_md5 == file_md5:
                    msg = "success"
                    self.request.sendall(bytes(msg, "utf8"))  # 发送成功消息
                    tools.color_msg(32, msg, 40)
                else:
                    msg = "incomplete"
                    self.request.sendall(bytes(msg, "utf8"))  # 发送残缺消息
                    tools.color_msg(32, msg, 40)

    def task_get(self, task_data, home_name):
        print("========",task_data)
        print("========",home_name)





    def task_register(self, task_data, home=None):
        """
        用户注册
        :param task_data:  用户的信息，类型字典
        :return:
        """
        print("sever_recv-------", task_data)
        user_name = task_data["name"]               #获取用户名
        user_size = task_data["user_size"]          #获取用户空间大小
        home_name = user_name + "_" + user_size     #拼接家目录名字
        # subprocess.call("mkdir %s" % home_name, shell=True)  #创建家目录
        subprocess.call("mkdir ../space/%s" % home_name, shell=True)  #创建家目录
        self.request.send(bytes("success", "utf8"))    #发送成功信息

if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(("0.0.0.0", 8899), Hftp)
    server.serve_forever()



    #注册出现为题
    #完成上传判断内文件大就报错
    # 说 server 没有 color_msg 模块 AttributeError: 'module' object has no attribute 'color_msg'